#include <stdlib.h>
#include "image_format.h"

Image ImageCreate(uint32_t width, uint32_t height, ImageFormat format)
{
    Image image;
    image.info.width = width;
    image.info.height = height;
    image.info.pb = format > IMAGE_FORMAT_BGR ? 4 : 3;
    image.info.size = image.info.width * image.info.height * image.info.pb;
    image.info.format = format;
    image.mem.addr = (uint8_t*)calloc(image.info.size, 1);
    return image;
}

void ImageDestory(Image image)
{
    if (image.mem.addr)
        free(image.mem.addr);
}

Image ImageTranslate(Image image, uint32_t width, uint32_t height, ImageFormat format)
{
    Image retImage = ImageCreate(width, height, format);
    ImageWrite(retImage, 0, 0, width, height, image);
    return retImage;
}

void ImageClear(Image image, uint32_t color)
{
    if (!image.mem.addr)
        return;
    
    ImagePointBGRA bgra = {
        (uint8_t)(color & 0xFF),
        (uint8_t)((color >> 8) & 0xFF),
        (uint8_t)((color >> 16) & 0xFF),
        (uint8_t)((color >> 24) & 0xFF)};
    ImagePointRGB rgb = {bgra.r, bgra.g, bgra.b};
    ImagePointBGR bgr = {bgra.b, bgra.g, bgra.r};
    ImagePointRGBA rgba = {bgra.r, bgra.g, bgra.b, bgra.a};
    ImagePointARGB argb = {bgra.a, bgra.r, bgra.g, bgra.b};
    ImagePointABGR abgr = {bgra.a, bgra.b, bgra.g, bgra.r};
    uint32_t count, countMax = image.info.width * image.info.height;
    ImageMem mem = image.mem;
    
    switch (image.info.format)
    {
    case IMAGE_FORMAT_RGB:
        for (count = 0; count < countMax; count++)
            *mem.rgb++ = rgb;
        break;
    case IMAGE_FORMAT_BGR:
        for (count = 0; count < countMax; count++)
            *mem.bgr++ = bgr;
        break;
    case IMAGE_FORMAT_RGBA:
        for (count = 0; count < countMax; count++)
            *mem.rgba++ = rgba;
        break;
    case IMAGE_FORMAT_BGRA:
        for (count = 0; count < countMax; count++)
            *mem.bgra++ = bgra;
        break;
    case IMAGE_FORMAT_ARGB:
        for (count = 0; count < countMax; count++)
            *mem.argb++ = argb;
        break;
    case IMAGE_FORMAT_ABGR:
        for (count = 0; count < countMax; count++)
            *mem.abgr++ = abgr;
        break;
    }
}

#define IMAGE_WRITE(format, array, r, g, b, a) \
if (a != 0xFF) {\
    switch (format) { \
    case IMAGE_FORMAT_RGB:\
    case IMAGE_FORMAT_RGBA:\
        if (!a) {\
            array[0] = r; array[1] = g; array[2] = b;\
        } else {\
            array[0] = (uint8_t)(((uint16_t)array[0] * a + (uint16_t)r * (0xFF - a)) >> 8);\
            array[1] = (uint8_t)(((uint16_t)array[1] * a + (uint16_t)g * (0xFF - a)) >> 8);\
            array[2] = (uint8_t)(((uint16_t)array[2] * a + (uint16_t)b * (0xFF - a)) >> 8);\
            if (format == IMAGE_FORMAT_RGBA && a < array[3]) array[3] = a;\
        } break;\
    case IMAGE_FORMAT_BGR:\
    case IMAGE_FORMAT_BGRA:\
        if (!a) {\
            array[0] = b; array[1] = g; array[2] = r;\
        } else {\
            array[0] = (uint8_t)(((uint16_t)array[0] * a + (uint16_t)b * (0xFF - a)) >> 8);\
            array[1] = (uint8_t)(((uint16_t)array[1] * a + (uint16_t)g * (0xFF - a)) >> 8);\
            array[2] = (uint8_t)(((uint16_t)array[2] * a + (uint16_t)r * (0xFF - a)) >> 8);\
        } if (format == IMAGE_FORMAT_BGRA && a < array[3]) array[3] = a;\
        break;\
    case IMAGE_FORMAT_ARGB:\
        if (!a) {\
            array[1] = r; array[2] = g; array[3] = b;\
        } else {\
            array[1] = (uint8_t)(((uint16_t)array[1] * a + (uint16_t)r * (0xFF - a)) >> 8);\
            array[2] = (uint8_t)(((uint16_t)array[2] * a + (uint16_t)g * (0xFF - a)) >> 8);\
            array[3] = (uint8_t)(((uint16_t)array[3] * a + (uint16_t)b * (0xFF - a)) >> 8);\
        } if (a < array[0]) array[0] = a;\
        break;\
    case IMAGE_FORMAT_ABGR:\
        if (!a) {\
            array[1] = b; array[2] = g; array[3] = r;\
        } else {\
            array[1] = (uint8_t)(((uint16_t)array[1] * a + (uint16_t)b * (0xFF - a)) >> 8);\
            array[2] = (uint8_t)(((uint16_t)array[2] * a + (uint16_t)g * (0xFF - a)) >> 8);\
            array[3] = (uint8_t)(((uint16_t)array[3] * a + (uint16_t)r * (0xFF - a)) >> 8);\
        } if (a < array[0]) array[0] = a;\
        break;\
    }\
}

void ImageWrite(Image image, int32_t x, int32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
    uint8_t *ptr = image.mem.addr + (y * image.info.width + x) * image.info.pb;
    IMAGE_WRITE(image.info.format, ptr, r, g, b, a);
}

void ImageWrite(Image image, int32_t x, int32_t y, uint32_t color)
{
    ImageWrite(image, x, y,
        (uint8_t)((color >> 16) & 0xFF),
        (uint8_t)((color >> 8) & 0xFF),
        (uint8_t)(color & 0xFF),
        (uint8_t)((color >> 24) & 0xFF));
}

void ImageWrite(Image image, int32_t x, int32_t y, uint32_t width, uint32_t height, Image plane)
{
    if (!image.mem.addr || !plane.mem.addr)
        return;

    if (x < (int32_t)image.info.width &&
        y < (int32_t)image.info.height &&
        x + (int32_t)width > 0 &&
        y + (int32_t)height > 0)
        ;
    else
        return;

    //image中的理论xy起始位置
    int32_t xBegin = x < 0 ? 0 : x;
    int32_t yBegin = y < 0 ? 0 : y;

    int32_t xEnd = x + width;
    int32_t yEnd = y + height;

    //image中的绘制矩形宽高
    int32_t xSize = xEnd > (int32_t)image.info.width ? image.info.width - xBegin : xEnd - xBegin;
    int32_t ySize = yEnd > (int32_t)image.info.height ? image.info.height - yBegin : yEnd - yBegin;

    //plane和image的行字节数
    int32_t plaLineSize = plane.info.width * plane.info.pb;
    int32_t imgLineSize = image.info.width * image.info.pb;

    //plane缩放计数分度格
    float xDiv = (float)plane.info.width / width; // olorImage横/纵向index每次增加量
    float yDiv = (float)plane.info.height / height;

    //plane和image的起始拷贝指针位置
    uint8_t *plaPtr = plane.mem.addr +
        ((int32_t)((yBegin - y) * yDiv) * plane.info.width + (int32_t)((xBegin - x) * xDiv)) * plane.info.pb;
    uint8_t *imgPtr = image.mem.addr +
        (yBegin * (int32_t)image.info.width + xBegin) * image.info.pb;

    //x/yStep用于plane的xy坐标计数,每次增加x/yDiv
    float xStep, yStep;
    //x/yC用于image的xy坐标计数
    int32_t xC, yC;

    int32_t xStepInt, yStepInt;

    for (yC = 0, yStep = 0; yC < ySize; yC++)
    {
        uint8_t *pPla = plaPtr;
        uint8_t *pImg = imgPtr;
        for (xC = 0, xStep = 0; xC < xSize; xC++)
        {            
            switch (plane.info.format)
            {
            case IMAGE_FORMAT_RGB:
                IMAGE_WRITE(image.info.format, pImg, pPla[0], pPla[1], pPla[2], 0)
                break;
            case IMAGE_FORMAT_BGR:
                IMAGE_WRITE(image.info.format, pImg, pPla[2], pPla[1], pPla[0], 0)
                break;
            case IMAGE_FORMAT_RGBA:
                IMAGE_WRITE(image.info.format, pImg, pPla[0], pPla[1], pPla[2], pPla[3])
                break;
            case IMAGE_FORMAT_BGRA:
                IMAGE_WRITE(image.info.format, pImg, pPla[2], pPla[1], pPla[0], pPla[3])
                break;
            case IMAGE_FORMAT_ARGB:
                IMAGE_WRITE(image.info.format, pImg, pPla[1], pPla[2], pPla[3], pPla[0])
                break;
            case IMAGE_FORMAT_ABGR:
                IMAGE_WRITE(image.info.format, pImg, pPla[3], pPla[2], pPla[1], pPla[0])
                break;
            }

            xStep += xDiv;
            //不满1,重复使用plane的该点
            if (xStep < 1.0f)
                ;
            //大于等于1,移动plane坐标
            else
            {
                xStepInt = (int32_t)xStep;
                xStep -= xStepInt;
                pPla += xStepInt * plane.info.pb;
            }
            pImg += image.info.pb;
        }

        yStep += yDiv;
        //不满1,重复使用plane的该行
        if (yStep < 1.0f)
            ;
        //大于等于1,移动plane行
        else
        {
            yStepInt = (int32_t)yStep;
            yStep -= yStepInt;
            plaPtr += plaLineSize * yStepInt;
        }
        imgPtr += imgLineSize;
    }
}

void ImageWrite(Image image, int32_t x, int32_t y, uint32_t width, uint32_t height, Film plane, int32_t index)
{
    Image tmp = *plane.image;
    tmp.mem.addr = plane.Addr(index);
    tmp.info.width = plane.width;
    tmp.info.height = plane.height;
    tmp.info.size = plane.size;
    ImageWrite(image, x, y, width, height, tmp);
}

#define IMAGE_COVER(format, array, r, g, b, a) \
switch (format) { \
case IMAGE_FORMAT_RGB: array[0] = r; array[1] = g; array[2] = b; break; \
case IMAGE_FORMAT_BGR: array[0] = b; array[1] = g; array[2] = r; break; \
case IMAGE_FORMAT_RGBA: array[0] = r; array[1] = g; array[2] = b; array[3] = a; break; \
case IMAGE_FORMAT_BGRA: array[0] = b; array[1] = g; array[2] = r; array[3] = a; break; \
case IMAGE_FORMAT_ARGB: array[0] = a; array[1] = r; array[2] = g; array[3] = b; break; \
case IMAGE_FORMAT_ABGR: array[0] = a; array[1] = b; array[2] = g; array[3] = r; break;}

void ImageCover(Image image, int32_t x, int32_t y, uint8_t r, uint8_t g, uint8_t b, uint8_t a)
{
    uint8_t *ptr = image.mem.addr + (y * image.info.width + x) * image.info.pb;
    IMAGE_COVER(image.info.format, ptr, r, g, b, a);
}

void ImageCover(Image image, int32_t x, int32_t y, uint32_t color)
{
    ImageCover(image, x, y,
        (uint8_t)((color >> 16) & 0xFF),
        (uint8_t)((color >> 8) & 0xFF),
        (uint8_t)(color & 0xFF),
        (uint8_t)((color >> 24) & 0xFF));
}

void ImageCover(Image image, int32_t x, int32_t y, uint32_t width, uint32_t height, Image plane)
{
    if (!image.mem.addr || !plane.mem.addr)
        return;

    if (x < (int32_t)image.info.width &&
        y < (int32_t)image.info.height &&
        x + (int32_t)width > 0 &&
        y + (int32_t)height > 0)
        ;
    else
        return;

    //image中的理论xy起始位置
    int32_t xBegin = x < 0 ? 0 : x;
    int32_t yBegin = y < 0 ? 0 : y;

    int32_t xEnd = x + width;
    int32_t yEnd = y + height;

    //image中的绘制矩形宽高
    int32_t xSize = xEnd > (int32_t)image.info.width ? image.info.width - xBegin : xEnd - xBegin;
    int32_t ySize = yEnd > (int32_t)image.info.height ? image.info.height - yBegin : yEnd - yBegin;

    //plane和image的行字节数
    int32_t plaLineSize = plane.info.width * plane.info.pb;
    int32_t imgLineSize = image.info.width * image.info.pb;

    //plane缩放计数分度格
    float xDiv = (float)plane.info.width / width; // olorImage横/纵向index每次增加量
    float yDiv = (float)plane.info.height / height;

    //plane和image的起始拷贝指针位置
    uint8_t *plaPtr = plane.mem.addr +
        ((int32_t)((yBegin - y) * yDiv) * plane.info.width + (int32_t)((xBegin - x) * xDiv)) * plane.info.pb;
    uint8_t *imgPtr = image.mem.addr +
        (yBegin * (int32_t)image.info.width + xBegin) * image.info.pb;

    //x/yStep用于plane的xy坐标计数,每次增加x/yDiv
    float xStep, yStep;
    //x/yC用于image的xy坐标计数
    int32_t xC, yC;

    int32_t xStepInt, yStepInt;

    for (yC = 0, yStep = 0; yC < ySize; yC++)
    {
        uint8_t *pPla = plaPtr;
        uint8_t *pImg = imgPtr;
        for (xC = 0, xStep = 0; xC < xSize; xC++)
        {            
            switch (plane.info.format)
            {
            case IMAGE_FORMAT_RGB:
                IMAGE_COVER(image.info.format, pImg, pPla[0], pPla[1], pPla[2], 0)
                break;
            case IMAGE_FORMAT_BGR:
                IMAGE_COVER(image.info.format, pImg, pPla[2], pPla[1], pPla[0], 0)
                break;
            case IMAGE_FORMAT_RGBA:
                IMAGE_COVER(image.info.format, pImg, pPla[0], pPla[1], pPla[2], pPla[3])
                break;
            case IMAGE_FORMAT_BGRA:
                IMAGE_COVER(image.info.format, pImg, pPla[2], pPla[1], pPla[0], pPla[3])
                break;
            case IMAGE_FORMAT_ARGB:
                IMAGE_COVER(image.info.format, pImg, pPla[1], pPla[2], pPla[3], pPla[0])
                break;
            case IMAGE_FORMAT_ABGR:
                IMAGE_COVER(image.info.format, pImg, pPla[3], pPla[2], pPla[1], pPla[0])
                break;
            }

            xStep += xDiv;
            //不满1,重复使用plane的该点
            if (xStep < 1.0f)
                ;
            //大于等于1,移动plane坐标
            else
            {
                xStepInt = (int32_t)xStep;
                xStep -= xStepInt;
                pPla += xStepInt * plane.info.pb;
            }
            pImg += image.info.pb;
        }

        yStep += yDiv;
        //不满1,重复使用plane的该行
        if (yStep < 1.0f)
            ;
        //大于等于1,移动plane行
        else
        {
            yStepInt = (int32_t)yStep;
            yStep -= yStepInt;
            plaPtr += plaLineSize * yStepInt;
        }
        imgPtr += imgLineSize;
    }
}

void ImageCover(Image image, int32_t x, int32_t y, uint32_t width, uint32_t height, Film plane, int32_t index)
{
    Image tmp = *plane.image;
    tmp.mem.addr = plane.Addr(index);
    tmp.info.width = plane.width;
    tmp.info.height = plane.height;
    tmp.info.size = plane.size;
    ImageCover(image, x, y, width, height, tmp);
}
